/****************************************************************************************
*	Mouzetrap - client/server framework	
*
*	$HeadURL: http://mouzetrap.googlecode.com/svn/trunk/Mouzetrap/ObjectPoolManager.cs $
*	$Id: ObjectPoolManager.cs 12 2009-06-12 10:49:38Z tobz@fallenrealms.net $
* 
*	Copyright (c) 2008 Toby Lawrence
* 
*	Permission is hereby granted, free of charge, to any person obtaining a copy
*	of this software and associated documentation files (the "Software"), to deal
*	in the Software without restriction, including without limitation the rights
*	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*	copies of the Software, and to permit persons to whom the Software is
*	furnished to do so, subject to the following conditions:
* 
*	The above copyright notice and this permission notice shall be included in
*	all copies or substantial portions of the Software.
* 
*	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*	THE SOFTWARE.
* 
****************************************************************************************/

using System;
using System.Collections.Generic;
using System.Threading;
using Mouzetrap.Threading;

namespace Mouzetrap
{
	/// <summary>
	/// Manages the creation of object pools.
	/// </summary>
	public static class ObjectPoolManager
	{
		private static Dictionary<Type, IObjectPool> _objectPools;
		private static SpinWaitLock _syncLock;

		/// <summary>
		/// Registers a type with the object pool.
		/// </summary>
		/// <typeparam name="T">the type of the object</typeparam>
		/// <param name="creator">delegate to create a new instance of the given type</param>
		/// <param name="initialCapacity">the initial capacity of the pool</param>
		/// <param name="dynamic">whether or not the pool is able to grow on demand</param>
		/// <returns>true if the pool was created; false if the pool already existed</returns>
		public static bool RegisterPool<T>(Func<T> creator, int initialCapacity, bool dynamic)
			where T : class
		{
			_syncLock.Enter();

			try
			{
				if(_objectPools.ContainsKey(typeof(T)))
					return false;

				// initialize our pool based on whether the user wants a fixed or growable pool
				IObjectPool pool;

				if(dynamic)
				{
					pool = new DynamicObjectPool<T>(creator, initialCapacity);
				}
				else
				{
					pool = new FixedObjectPool<T>(initialCapacity);
				}

				// fill our pool initially
				for(int i = 0; i < initialCapacity; i++)
				{
					pool.Fill(creator());
				}

				// internal dictionary is treated immutably so that we don't have to lock to
				// acquire pools when we want to get or put an object
				Dictionary<Type, IObjectPool> dict;
				Dictionary<Type, IObjectPool> dict2;

				do
				{
					dict = _objectPools;
					dict2 = new Dictionary<Type, IObjectPool>(dict) { { typeof(T), pool } };
				} while(Interlocked.CompareExchange(ref _objectPools, dict2, dict) != dict);
			}
			finally
			{
				_syncLock.Exit();
			}

			return true;
		}

		/// <summary>
		/// Removes a type from the object pool.
		/// </summary>
		/// <typeparam name="T">the type to remove</typeparam>
		/// <returns>true if the type was removed; false if the type wasn't registered</returns>
		public static bool UnregisterPool<T>()
		{
			_syncLock.Enter();

			try
			{
				if(!_objectPools.ContainsKey(typeof(T)))
					return false;

				// internal dictionary is treated immutably so that we don't have to lock to
				// acquire pools when we want to get or put an object
				Dictionary<Type, IObjectPool> dict;
				Dictionary<Type, IObjectPool> dict2;

				do
				{
					dict = _objectPools;

					dict2 = new Dictionary<Type, IObjectPool>(dict);
					dict2.Remove(typeof(T));
				} while(Interlocked.CompareExchange(ref _objectPools, dict2, dict) != dict);
			}
			finally
			{
				_syncLock.Exit();
			}

			return true;
		}

		/// <summary>
		/// Gets an object from the object pool.
		/// </summary>
		/// <typeparam name="T">the type of object to get</typeparam>
		/// <returns>an object of the given type</returns>
		public static T GetObject<T>()
			where T : class
		{
			IObjectPool pool;

			if(!_objectPools.TryGetValue(typeof(T), out pool))
				return default(T);

			return (T) pool.Drain();
		}

		/// <summary>
		/// Returns an object to the object pool.
		/// </summary>
		/// <typeparam name="T">the type of the object to return</typeparam>
		/// <param name="obj">the object to return</param>
		public static void ReturnObject<T>(T obj)
		{
			IObjectPool pool;

			if(!_objectPools.TryGetValue(typeof(T), out pool))
				return;

			pool.Fill(obj);
		}
	}
}